import subprocess
import re
import os
import argparse

def compile_rust_pro():
    gpt_pro = "gpt-pro"
    current_dir = os.getcwd()
    try:
        os.chdir(gpt_pro)
        env = os.environ.copy()
        env['RUSTFLAGS'] = '-Awarnings'
        #result = subprocess.run(['cargo', 'build'], shell=True, capture_output=True, text=True, env=env)
        process = subprocess.Popen(['cargo', 'build'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=env)
        stdout, stderr = process.communicate(timeout=30)
        print(f"stdout:\n{stdout}")
        print(f"stderr:\n{stderr}")
        if process.returncode!=0:
            errors = stderr
        else:
            errors = ""
        error_list = []
        if errors:
            error_list = extract_error_locations(errors)
            if not error_list:
                print("Errors:")
                print(errors)
                error_list = ["Not invalid errors"]
        else:
            pass
    finally:
        os.chdir(current_dir)
    return error_list

def run_rust_pro(c_input=""):
    gpt_pro = "gpt-pro"
    current_dir = os.getcwd()
    has_error = False
    cargo_output = ""
    try:
        os.chdir(gpt_pro)
        env = os.environ.copy()
        env['RUSTFLAGS'] = '-Awarnings'

        print(f"cargo run dir:{os.getcwd()}")
        if c_input:
            process = subprocess.Popen(['cargo', 'run'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=env)
            stdout, stderr = process.communicate(input=c_input, timeout=30)
        else:
            process = subprocess.Popen(['cargo', 'run'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=env)
            stdout, stderr = process.communicate(timeout=30)
        if process.returncode!=0:
            cargo_output = stderr
            has_error = True
        else:
            cargo_output = stdout
    except subprocess.TimeoutExpired as e:
        process.kill()
        has_error = True
        cargo_output = f"TimeoutExpired:{e}"
    finally:
        os.chdir(current_dir)
    return has_error, cargo_output

def cargo_check_rust_file(rust_file_path):
    gpt_pro = "single-check"
    check_path = os.path.join(gpt_pro, "src", "check.rs")
    with open(rust_file_path, "r") as rfile, open(check_path, "w") as cfile:
        code = rfile.read()
        cfile.write(code)
    current_dir = os.getcwd()
    try:
        os.chdir(gpt_pro)
        env = os.environ.copy()
        env['RUSTFLAGS'] = '-Awarnings'
        process = subprocess.Popen(['cargo', 'check'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=env)
        stdout, stderr = process.communicate(timeout=50)
        print(f"stdout:\n{stdout}")
        print(f"stderr:\n{stderr}")
        if process.returncode!=0:
            errors = stderr
        else:
            errors = ""
        error_list = []
        if errors:
            error_list = extract_error_locations(errors)
            filtered_error_list = []
            for error in error_list:
                description = error["description"]
                error_detail = error["error_details"]
                error_detail = description + error_detail
                if "unresolved import" in error_detail or \
                        "cannot find" in error_detail or \
                        "use of undeclared crate or module" in error_detail or \
                        "failed to resolve" in error_detail or \
                        "not found in this scope" in error_detail or \
                        "consider adding a `main` function" in error_detail or \
                        "could not find" in error_detail or \
                        "use of undeclared type" in error_detail or \
                        "implicitly returns `()` as its body has no tail or `return` expression" in error_detail or \
                        error_detail.startswith("help: "):
                    continue
                filtered_error_list.append(error)

            if not filtered_error_list:
                print("Errors:")
                print(errors)
                filtered_error_list = ["Not invalid errors"]
        else:
            filtered_error_list = error_list
    finally:
        os.chdir(current_dir)
    return filtered_error_list


def rustc_compile_rust_file(rust_file_path):
    command = f"rustc -Awarnings {rust_file_path}"
    result = subprocess.run(command, shell=True, capture_output=True)
    if result.returncode!=0:
        errors = result.stderr.decode()
    else:
        errors = ""
    #errors = stderr.decode('utf-8')
    error_list = []
    if errors:
        #print(f"Compilation Errors:")
        #print(errors)
        error_list = extract_error_locations(errors)
        #if error_list:
        #    print("Extract error Info!")
        #    print(error_list)
        #else:
        #    print("Fail to extract error info!")
        #    print(f"Errors:\n{errors}")
        if not error_list:
            print(f"{errors}")
            error_list = ["Not invalid errors"]
        
    else:
        #print("Compilation successful with no errors or warnings.")
        pass
    return error_list

def extract_error_locations(error_message):
    # Ignore the entire warning paragraph
    filtered_message = re.sub(r"(?m)^warning:.*?(?=(^warning:|^error\[|\Z))", "", error_message, flags=re.DOTALL)
    #print(filtered_message) 
    # Match starting with 'error' and retrieve all content up to the first blank line
    # error line, column
    #pattern = r"(?m)^error\[.*?\](?:.*?\n)+?.*?--> (.*?):(\d+):(\d+)"
    # error line, column, code
    #pattern = r"(?m)^error\[\w+\]:.*?(?:\n|.)*?--> (.*?):(\d+):(\d+)\n(?:.|\n)+?\|(.+)"
    #pattern = r"(?m)^error\[\w+\]:.*?--> (.*?):(\d+):(\d+)(.*?)(?=\n\s*\n|\Z)"
    pattern = r"(?m)^error(?:\[E\d+\])?:(.*?)\n\s*--> (.*?):(\d+):(\d+)(.*?)(?=\n\s*\n|\Z)"
    #matches = re.findall(pattern, filtered_message, re.DOTALL)
    matches = re.finditer(pattern, filtered_message, re.DOTALL)
    error_list = []
    if matches:
        #print("Wrong location found:")
        #for file_path, line_number, column_number, error_details in matches:
        #    print(f"File: {file_path}, Line: {line_number}, Column{column_number}")
        #    print(f"Related codes and descriptions: {error_details.strip()}")
        #    #additional_info = ""
        #    #if additional_info:
        #    #    print(f"Detailed error information: \n{additional_info.strip()}") 
        #    error_info = {
        #    "file_path": file_path,
        #    "line_number": line_number,
        #    "column_number": column_number,
        #    "error_details": error_details.strip()
        #    }
        #    error_list.append(error_info)
        for match in matches:
            description, file_path, line_number, column_number, error_details = match.groups()
            #print(f"Error details: {error_details}")
            #if "error:" not in error_details and "error[" not in error_details:
            #    continue
            error_info = {
                "description": description,
                "file_path": file_path,
                "line_number": line_number,
                "column_number": column_number,
                "error_details": error_details.strip()
            }
            error_list.append(error_info)
    return error_list
        
def main():
    parser = argparse.ArgumentParser(description='Compile a Rust file and extract error details.')
    parser.add_argument('rust_file_path', type=str, help='The path to the Rust file to compile.')
    args = parser.parse_args()

    errors = rustc_compile_rust_file(args.rust_file_path)
    if len(errors) > 0:
        print("Details of errors found:")
        for error in errors:
            print(f"File: {error['file_path']}, Line: {error['line_number']}, Column: {error['column_number']}")
            print("Detailed error information:")
            print(error['error_details'])
    else:
        print("No compilation errors.")

# if __name__ == "__main__":
#     #main()
#     import json
#     error_list = cargo_check_rust_file("tmp.rs")

#     for error in error_list:
#         formated_error = json.dumps(error, indent=4)
#         print(f"Rust errors return:\n---\n{error_list}---\n")
#         print(formated_error)


def test_rust_error():
    import sys, json
    
    # 设置测试 Rust 文件路径
    test_rust_file = "test_files/rust/test.rs"
    
    # 模拟命令行参数
    sys.argv = [
        "rust_error.py",
        test_rust_file
    ]
    
    # 调用原main函数的逻辑
    parser = argparse.ArgumentParser(description='Compile a Rust file and extract error details.')
    parser.add_argument('rust_file_path', type=str, help='The path to the Rust file to compile.')
    
    args = parser.parse_args()
    
    # 使用不同的错误检查方法
    print("Testing rustc compile:")
    errors = rustc_compile_rust_file(args.rust_file_path)
    if len(errors) > 0:
        print("\nDetails of rustc errors found:")
        for error in errors:
            print(f"File: {error['file_path']}")
            print(f"Line: {error['line_number']}")
            print(f"Column: {error['column_number']}")
            print("Error details:")
            print(error['error_details'])
            print("---")
    
    print("\nTesting cargo check:")
    error_list = cargo_check_rust_file(args.rust_file_path)
    for error in error_list:
        print("\nCargo check error details:")
        print(json.dumps(error, indent=4))

if __name__ == "__main__":
    test_rust_error()